package viper.ji;

/***************************************************
 * VIPER.java
 * -----------------------
 * Revision: v.1.0
 * Author: Ji Kim
 * Date Created: 12/05/2009
 * Description:
 *   Preliminary sender/receiver program that captures
 *   and digitizes audio from a microphone to create
 *   an audio data stream to be used for diversity
 *   coding and transmission.
 *   Also contains debug code for default path VoIP
 *   testing and diagnostics.
 * Last Back-Up: None, first revision
 * Revision History: None
 * 
 * Based on MediaTransmitter.java demo source code
 * in JMF package. This can be found in the link
 * provided in the tutorial at:
 * http://carfield.com.hk/document/java/tutorial
 *    /jmf_tutorial.pdf
 * -----------------------  
 ***************************************************/

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.media.*;
import javax.media.control.*;
import javax.media.protocol.*;
import javax.media.format.*;

import java.io.*;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Vector;

public class VIPER {

	public MediaLocator mediaLocator = null;
	public DataSink dataSink = null;
	public Processor mediaProcessor = null;
	
	private DataSource digitizedAudioSource;
	
	private InputStream is;
	private Buffer inBuffer;
	
	//Define audio codec type
	private static final Format[] FORMATS = new Format[]{
		new AudioFormat(AudioFormat.G723) //Set to ULAW_RTP to test with RTP session
		};
	//Define output of Processor
	private static final ContentDescriptor CONTENT_DESCRIPTOR =
		new ContentDescriptor(ContentDescriptor.RAW);

	//Constructor, define destination IP
	public VIPER(MediaLocator locator) {
		mediaLocator = locator;
	}

	public void startEncoding() throws IOException {
		mediaProcessor.start(); //Start the Processor: encode audio data by defined codec
	}
	
	public void startTransmitting() throws IOException {
		dataSink.open();
		dataSink.start();
		
		digitizedAudioSource.start(); //Start streaming encoded audio data
	}

	public void setDataSource(DataSource ds) throws IOException,
	NoProcessorException, CannotRealizeException {
		
		mediaProcessor = Manager.createRealizedProcessor(
				new ProcessorModel(ds, FORMATS, CONTENT_DESCRIPTOR));
		
		//Get the digitized and encoded data stream
		digitizedAudioSource = mediaProcessor.getDataOutput();

	}
	
	public void setDataSink(DataSource ds) throws IOException,
	NoDataSinkException {
		
		dataSink = Manager.createDataSink(ds,
				mediaLocator);
		
	}
	
	//Debug method to analyze every Buffer in audio stream
	public LiveStream extract(InetAddress ip, int port) throws IOException{
		Extractor ex = new Extractor(digitizedAudioSource, ip, port);
		ex.start();
		return ex.getLiveStream();
	}
	
	public DataSource getSource(){
		return digitizedAudioSource;
	}
	
	public static void main(String [] args) {
		
		DataSource source = null;
		PushBufferStream pbs = null;
		Player player = null;
		LiveStream ls;
		
		DatagramSocket rxsock = null;
		
		int maxPacketSize = 9000;
		InetAddress destIP = null;
		int destPort;
		try{
			destIP = InetAddress.getByName(args[0]);
		}catch(Exception uhe){
			System.err.println("Unknown Host Name.");
		}
		destPort = Integer.valueOf(args[1]);

		try {
			
			//Set final destination IP and port
			//i.e.) 192.168.0.1:1899
			MediaLocator locator = new MediaLocator("rtp://"+args[0]+"/audio");
			
			VIPER transmitter = new VIPER(locator);
			System.out.println("-> Created media locator: '" + locator + "'");
			
			//Set type of audio format expected from capture device
			AudioFormat format = new AudioFormat(AudioFormat.LINEAR, //encoding type
					44100, //sampling frequency 
					16, //sample depth
					2); //channels
			
			//Get a list of connected capture devices and set the first available one
			//as the designated device
			Vector devices = CaptureDeviceManager.getDeviceList(format);
			CaptureDeviceInfo cdi = (CaptureDeviceInfo) devices.elementAt(0);
			
			//Create DataSource from capture device
			source = Manager.createDataSource(cdi.getLocator());

			transmitter.setDataSource(source);
			System.out.println("-> Set the data source on the transmitter");

			transmitter.startEncoding();
			System.out.println("-> Encoding...");
			
			//Extract data stream from DataSource object
			ls = transmitter.extract(destIP, destPort);
			System.out.println("-> Transmitting...");
			//PushBufferStream [] streams = ((PushBufferDataSource)transmitter.getSource()).getStreams();
			
			//CustomDataSource cds = new CustomDataSource(ls);
						
			//Construct custom DataSource from extracted data stream
			//PBDataSource pbds = new PBDataSource(streams[0]);
			
			//Enable DataSource
			//pbds.connect();
			//cds.connect();
			
			/** Debug code to transmit stream over RTP session **/
			/*
			transmitter.setDataSink(pbds);
			System.out.println("-> Set the data sink from Buffer stream");
			
			transmitter.startTransmitting();
			System.out.println("-> Transmitting...");
			*/
			
			/** Debug code to play audio stream locally **/
			/*
			player = Manager.createRealizedPlayer(cds);
			
			transmitter.getSource().start();
			
			player.start();
			
			System.out.println("-> Playback Audio...");
			*/
			
			/** Receiver code **/
			/*
			try{
				rxsock = new DatagramSocket(destPort);
				System.out.println("Initializing socket.");
			}catch(Exception e){
				System.out.println("Failed to open socket.");
			}
			
			LiveStream rxls = new LiveStream();
			CustomDataSource rxcds = new CustomDataSource(rxls);
			rxcds.connect();
			
			player = Manager.createRealizedPlayer(rxcds);
			player.start();
			System.out.println("Starting player...");
			
			//transmitter.getSource().start();
			
			byte[] buff = new byte[maxPacketSize];
			
			while(true){
				DatagramPacket p = new DatagramPacket(buff, maxPacketSize);
				try{
					rxsock.receive(p);
					System.out.println("Reading...");
				}catch(IOException ioe){
					System.out.println("IOException during receiving packets.");
				}
				//update with buff only if something was received...
				if(p!=null){
					System.out.println("Packet Received!!");
					rxls.update(buff);
				}
			}
			*/
			/*
			player = Manager.createRealizedPlayer(locator);
			
			player.start();
			*/
			
		} catch (Throwable t) {
			t.printStackTrace();
		}
	}

}